; *****************************************************************************
; aes-256.fasm
; *****************************************************************************

; Copyright © 2018 Michael Mamic
; ----------------------------------------------------
; Permission is hereby granted, free of charge,
; to any person obtaining a copy of this software
; and associated documentation files (the "Software"),
; to deal in the Software without restriction,
; including without limitation the rights to use,
; copy, modify, merge, publish, distribute, sublicense,
; and/or sell copies of the Software,
; and to permit persons to whom the Software is furnished to do so,
; subject to the following conditions:
;
; The above copyright notice and this permission notice shall be
; included in all copies or substantial portions of the Software.

; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
; TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; -----------------------------------------------------
; The code below provides two functions,
; AESEncryptSector and AESDecryptSector,
; which each need a key, a buffer, and a sector LBA,
; to perform a CBC AES256 encryption or decryption on
; the given sector.
; -----------------------------------------------------
; The code was designed to use only the simplest instructions:
; mov, push, pop, xor, inc, dec, cmp, sub, rol ... etc
; In order to be easily portable for more restricted architectures.
; -----------------------------------------------------
; The block of constants used at the bottom of the code
; was generated by a C# .NET Console program based on the information
; found at https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

use16

loadDX:
push di
push ds
mov di, 0x5F0
mov ds, di
xor di, di
mov dx, word [ds:di]
pop ds
pop di
ret

; AES Implementation: Clear all memory used by AES
AESClearMemory:
push di
push ds
mov di, 0x602
mov ds, di
xor di, di
    AESClearMemory_ClearLoop:
    mov [ds:di], byte 0
    inc di
    cmp di, 0x4E0
    jne AESClearMemory_ClearLoop
AESClearMemory_end:
pop ds
pop di
ret

; # AES Implementation Encrypt Sector CBC (Unoptimized) #
; Input: CX = Sector, Key at 0x6000:0x0, Memory at [ds:di]. DI should be 0 to begin with.
AESEncryptSector:
; Generate IV at 0x60E0
pusha
push ds
push es
mov bx, 0x60E
mov es, bx
xor bx, bx
mov di, 0x630
mov ds, di
xor di, di
AESEncryptSector_GenerateIVLoop:
mov al, 0x5A
;xor al, al
;rdrand ax
mov [ds:di], byte al
mov [es:bx], byte al
inc di
inc bx
cmp bx, 0x10
jne AESEncryptSector_GenerateIVLoop
pop es
pop ds
popa
pusha
; Times 32 (32 * 16 = 512) Encrypt each block by the last one.
push cx
add di, 0x10
xor cx, cx
    ; Copy memory to 0x60E0 + 0x10 (IV is at 0x60E0)
    AESEncryptSector_EncryptBlockLoop:
    pusha
    push es
    mov bx, 0x60F
    mov es, bx
    xor bx, bx
    xor cx, cx
	AESEncryptSector_EncryptBlockLoop_LoadBlock:
	mov al, byte [ds:di]
	mov [es:bx], byte al
	inc di
	inc bx
	inc cx
	cmp cx, 0x10
	jne AESEncryptSector_EncryptBlockLoop_LoadBlock
    pop es
    popa
    ; XOR by previous block
    pusha
    push ds
    push es
    mov bx, 0x630
    mov ds, bx
    mov bx, 0x60F
    mov es, bx
    xor cx, cx
    xor bx, bx
    sub di, 0x10
	AESEncryptSector_EncryptBlockLoop_XORByLastBlock:
	mov al, byte [es:bx]
	mov ah, byte [ds:di]
	xor al, ah
	mov [es:bx], byte al
	;mov [ds:di], byte al
	inc bx
	inc di
	inc cx
	cmp cx, 0x10
	jne AESEncryptSector_EncryptBlockLoop_XORByLastBlock
    pop es
    pop ds
    popa
    ; Copy 0x60F0 to 0x60E0
    pusha
    push ds
    mov di, 0x600
    mov ds, di
    mov di, 0xE0
    mov si, 0xF0
	AESEncryptSector_EncryptBlockLoop_CopyToEncryptBufferLoop:
	mov al, byte [ds:si]
	mov [ds:di], byte al
	inc si
	inc di
	cmp di, 0xF0
	jne AESEncryptSector_EncryptBlockLoop_CopyToEncryptBufferLoop
    pop ds
    popa
    ; Call AESEncryptBlock
    call AESEncryptBlock
    ; Copy 0x60E0 to 0x60F0
    pusha
    push ds
    mov di, 0x600
    mov ds, di
    mov di, 0xE0
    mov si, 0xF0
	AESEncryptSector_EncryptBlockLoop_CopyFromEncryptBufferLoop:
	mov al, byte [ds:di]
	mov [ds:si], byte al
	inc si
	inc di
	cmp di, 0xF0
	jne AESEncryptSector_EncryptBlockLoop_CopyFromEncryptBufferLoop
    pop ds
    popa
    ; Copy memory to 0x6300
    pusha
    push es
    push ds
    mov bx, 0x60F
    mov es, bx
    mov bx, 0x630
    mov ds, bx
    xor bx, bx
    xor cx, cx
	AESEncryptSector_EncryptBlockLoop_UnloadBlock:
	mov al, byte [es:bx]
	mov [ds:di], byte al
	inc di
	inc bx
	inc cx
	cmp cx, 0x10
	jne AESEncryptSector_EncryptBlockLoop_UnloadBlock
    pop ds
    pop es
    popa
    add di, 0x10
    inc cx
    cmp cx, 31
    jne AESEncryptSector_EncryptBlockLoop
pop cx
; Write Sector
call loadDX
; ToDo: call convertCXToTrackSector
mov bx, 0x630
mov es, bx
mov bx, 0x0

AESEncryptSector_checkSectorWrite:
mov ah, 0x3
mov al, 0x1
int 0x13
jc AESEncryptSector_checkSectorWrite
AESEncryptSector_end:
popa
ret


; # AES Implementation Decrypt Sector CBC (Unoptimized) #
; Input: CX = Sector, Key at 0x6000:0x0, Memory at [ds:di]. DI should be 0 to begin with.
AESDecryptSector:
; Load Sector
call loadDX
; ToDo: call convertCXToTrackSector
mov bx, 0x630
mov es, bx
mov bx, 0x0

AESDecryptSector_checkSectorRead:
mov ah, 0x2
mov al, 0x1
int 0x13
jc AESDecryptSector_checkSectorRead
pusha
push ds
; Times 32 (32 * 16 = 512) Encrypt each block by the last one.
push cx
mov di, 0x630
mov ds, di
mov di, 0x200
xor cx, cx
; Decrypt:
; Copy memory from block.
; Decrypt.
; XOR block by previous one.
; Copy memory to 0x60E0
    AESDecryptSector_DecryptBlockLoop:
    pusha
    push es
    mov bx, 0x60E
    mov es, bx
    mov bx, 0x630
    mov ds, bx
    xor bx, bx
    xor cx, cx
	AESDecryptSector_DecryptBlockLoop_LoadBlock:
	mov al, byte [ds:di]
	mov [es:bx], byte al
	inc di
	inc bx
	inc cx
	cmp cx, 0x10
	jne AESDecryptSector_DecryptBlockLoop_LoadBlock
    xor bx, bx
    pop es
    popa
    ; Call AESDecryptBlock
    call AESDecryptBlock
    ; XOR block by previous one.
    pusha
    push ds
    push es
    mov bx, 0x630
    mov ds, bx
    mov bx, 0x60E
    mov es, bx
    xor cx, cx
    xor bx, bx
    sub di, 0x10
	AESDecryptSector_DecryptBlockLoop_XORByLastBlock:
	mov al, byte [es:bx]
	mov ah, byte [ds:di]
	xor al, ah
	mov [es:bx], byte al
	inc bx
	inc di
	inc cx
	cmp cx, 0x10
	jne AESDecryptSector_DecryptBlockLoop_XORByLastBlock
    pop es
    pop ds
    popa
    ; Copy memory to 0x6300
    pusha
    push es
    push ds
    mov bx, 0x60E
    mov es, bx
    xor bx, bx
    xor cx, cx
	AESDecryptSector_DecryptBlockLoop_UnloadBlock:
	mov al, byte [es:bx]
	mov [ds:di], byte al
	inc di
	inc bx
	inc cx
	cmp cx, 0x10
	jne AESDecryptSector_DecryptBlockLoop_UnloadBlock
    pop ds
    pop es
    popa
    sub di, 0x10
    inc cx
    cmp cx, 32
    jne AESDecryptSector_DecryptBlockLoop
; Memory decrypted at 0x6300. Copy it to [DS:DI]
pop cx
pop ds
popa
pusha
push es
mov bx, 0x631
mov es, bx
xor bx, bx
xor cx, cx
    AESDecryptSector_CopyMemoryToDSDI:
    mov al, byte [es:bx]
    mov [ds:di], byte al
    inc bx
    inc di
    inc cx
    cmp cx, 0x1F0
    jne AESDecryptSector_CopyMemoryToDSDI
AESDecryptSector_end:
pop es
popa
ret



; AES Implementation Encrypt Block (Unoptimized)
; Input: Plaintext at 0x60E0, Key at 0x6000.
; Output: Ciphertext at 0x60E0
AESEncryptBlock:
pusha
push ds
push es
; Generate Key Schedule
call GenerateKeySchedule
; Move plaintext to memory after the keys
mov bx, 0x50 ; Move pointer to the plaintext.
mov es, bx
xor bx, bx
mov di, 0x600
mov ds, di
mov di, 0xE0
xor si, si
xor cx, cx
    AESEncryptBlock_XORPlaintextByFirstKeyLoop: ; XOR the plaintext by the first key.
    mov ax, word [ds:si]
    mov bx, word [ds:di]
    xor ax, bx
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    inc cx
    cmp cx, 0x8
    jne AESEncryptBlock_XORPlaintextByFirstKeyLoop
mov di, 0xE0
mov si, 0x20
xor cx, cx
    AESEncryptBlock_EncryptBlockRoundLoop: ; Loop to encrypt the block.
    ; A B C D  Row 0
    ; B C D A  Row 1
    ; C D A B  Row 2
    ; D A B C  Row 3
    push cx
    push di
    push si
    ; Shift the plaintext
    mov di, 0xE0
    add di, 0x4 ; Second Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    dec di
    mov [ds:di], byte bh
    dec di
    mov [ds:di], byte al
    add di, 0x4 ; Third Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte al
    dec di
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    dec di
    mov [ds:di], byte bh
    add di, 0x4 ; Fourth Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte bh
    dec di
    mov [ds:di], byte al
    dec di
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    ; Run the plaintext through the S-Box
    sub di, 12
    xor cx, cx
    xor ah, ah
	AESEncryptBlock_EncryptBlockRoundLoop_RunThroughSBoxLoop: ; Run each column through the S-Box
	mov al, byte [ds:di]
	xor ah, ah
	rol al, 4
	mov bx, SBox
	add bx, ax
	mov al, byte [es:bx]
	mov [ds:di], byte al
	inc cx
	inc di
	cmp cx, 0x10
	jne AESEncryptBlock_EncryptBlockRoundLoop_RunThroughSBoxLoop
    ; MixColumns on the plaintext
    call MixColumns
    pop si
    ; XOR plaintext by the current key
    pusha
    mov di, 0xE0
    xor cx, cx
	AESEncryptBlock_EncryptBlockRoundLoop_XORPlaintextByCurrentKeyLoop: ; XOR the plaintext by the current key.
	mov ax, word [ds:si]
	mov bx, word [ds:di]
	xor ax, bx
	mov [ds:di], word ax
	add si, 0x2
	add di, 0x2
	inc cx
	cmp cx, 0x8
	jne AESEncryptBlock_EncryptBlockRoundLoop_XORPlaintextByCurrentKeyLoop
    popa
    pop di
    pop cx
    mov di, 0xE0
    inc cx
    add si, 0x10
    cmp cx, 12
    jne AESEncryptBlock_EncryptBlockRoundLoop
; The loop is now done. Now do the final step, which is everything minus the MixColumns function
; Shift the plaintext
mov di, 0xE0
add di, 0x4 ; Second Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
dec di
mov [ds:di], byte bh
dec di
mov [ds:di], byte al
add di, 0x4 ; Third Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte al
dec di
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
dec di
mov [ds:di], byte bh
add di, 0x4 ; Fourth Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte bh
dec di
mov [ds:di], byte al
dec di
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
; Run the plaintext through the S-Box
sub di, 12
xor cx, cx
xor ah, ah
    AESEncryptBlock_RunThroughSBoxLoop: ; Run each column through the S-Box
    mov al, byte [ds:di]
    xor ah, ah
    rol al, 4
    mov bx, SBox
    add bx, ax
    mov al, byte [es:bx]
    mov [ds:di], byte al
    inc cx
    inc di
    cmp cx, 0x10
    jne AESEncryptBlock_RunThroughSBoxLoop
; XOR plaintext by the last key
mov di, 0xE0
mov si, 0xD0
xor cx, cx
    AESEncryptBlock_XORPlaintextByLastKeyLoop: ; XOR the plaintext by the last key.
    mov ax, word [ds:si]
    mov bx, word [ds:di]
    xor ax, bx
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    inc cx
    cmp cx, 0x8
    jne AESEncryptBlock_XORPlaintextByLastKeyLoop
AESEncryptBlock_end:
pop es
pop ds
popa
ret

; AES Implementation Decrypt Block (Unoptimized)
AESDecryptBlock:
pusha
push ds
push es
; Generate Key Schedule
call GenerateKeySchedule
; Move plaintext to memory after the keys
mov bx, 0x50 ; Move pointer to the plaintext.
mov es, bx
xor bx, bx
mov di, 0x600
mov ds, di
mov di, 0xE0
mov si, 0xD0
xor cx, cx
    AESDecryptBlock_XORPlaintextByLastKeyLoop: ; XOR the plaintext by the last key.
    mov ax, word [ds:si]
    mov bx, word [ds:di]
    xor ax, bx
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    inc cx
    cmp cx, 0x8
    jne AESDecryptBlock_XORPlaintextByLastKeyLoop
mov bx, 0x50
mov es, bx
mov di, 0xE0
; Run the plaintext through the S-Box
xor cx, cx
xor ah, ah
    AESDecryptBlock_RunThroughSBoxLoop: ; Run each column through the S-Box
    mov al, byte [ds:di]
    xor ah, ah
    rol al, 4
    mov bx, InverseSBox
    add bx, ax
    mov al, byte [es:bx]
    mov [ds:di], byte al
    inc cx
    inc di
    cmp cx, 0x10
    jne AESDecryptBlock_RunThroughSBoxLoop
; Shift the plaintext
; A B C D  Row 0
; D A B C  Row 1
; C D A B  Row 2
; B C D A  Row 3
mov di, 0xE0
add di, 0x4 ; Second Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte bh
dec di
mov [ds:di], byte al
dec di
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
add di, 0x4 ; Third Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte al
dec di
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
dec di
mov [ds:di], byte bh
add di, 0x4 ; Fourth Row
mov ah, byte [ds:di]
inc di
mov al, byte [ds:di]
inc di
mov bh, byte [ds:di]
inc di
mov bl, byte [ds:di]
mov [ds:di], byte ah
dec di
mov [ds:di], byte bl
dec di
mov [ds:di], byte bh
dec di
mov [ds:di], byte al
; Loop 12 times
mov di, 0xE0
mov si, 0xD0
xor cx, cx
    AESDecryptBlock_DecryptBlockRoundLoop: ; Loop to encrypt the block.
    push di
    push si
    push cx
    pusha
    mov di, 0xE0
    xor cx, cx
	AESDecryptBlock_DecryptBlockRoundLoop_XORPlaintextByCurrentKeyLoop: ; XOR the plaintext by the first key.
	mov ax, word [ds:si]
	mov bx, word [ds:di]
	xor ax, bx
	mov [ds:di], word ax
	add si, 0x2
	add di, 0x2
	inc cx
	cmp cx, 0x8
	jne AESDecryptBlock_DecryptBlockRoundLoop_XORPlaintextByCurrentKeyLoop
    popa
    ; MixColumns on the plaintext
    call MixColumnsInverse
    ; Run the plaintext through the S-Box
    xor cx, cx
    xor ah, ah
	AESDecryptBlock_DecryptBlockRoundLoop_RunThroughSBoxLoop: ; Run each column through the S-Box
	mov al, byte [ds:di]
	xor ah, ah
	rol al, 4
	mov bx, InverseSBox
	add bx, ax
	mov al, byte [es:bx]
	mov [ds:di], byte al
	inc cx
	inc di
	cmp cx, 0x10
	jne AESDecryptBlock_DecryptBlockRoundLoop_RunThroughSBoxLoop
    ; Shift the plaintext
    ; A B C D  Row 0
    ; D A B C  Row 1
    ; C D A B  Row 2
    ; B C D A  Row 3
    mov di, 0xE0
    add di, 0x4 ; Second Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte bh
    dec di
    mov [ds:di], byte al
    dec di
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    add di, 0x4 ; Third Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte al
    dec di
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    dec di
    mov [ds:di], byte bh
    add di, 0x4 ; Fourth Row
    mov ah, byte [ds:di]
    inc di
    mov al, byte [ds:di]
    inc di
    mov bh, byte [ds:di]
    inc di
    mov bl, byte [ds:di]
    mov [ds:di], byte ah
    dec di
    mov [ds:di], byte bl
    dec di
    mov [ds:di], byte bh
    dec di
    mov [ds:di], byte al
    pop cx
    pop si
    pop di
    mov di, 0xE0
    sub si, 0x10
    inc cx
    cmp cx, 12
    jne AESDecryptBlock_DecryptBlockRoundLoop
; XOR plaintext by the first key
mov di, 0x600
mov ds, di
mov di, 0xE0
xor si, si
xor cx, cx
    AESDecryptBlock_XORPlaintextByFirstKeyLoop: ; XOR the plaintext by the first key.
    mov ax, word [ds:si]
    mov bx, word [ds:di]
    xor ax, bx
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    inc cx
    cmp cx, 0x8
    jne AESDecryptBlock_XORPlaintextByFirstKeyLoop
AESDecryptBlock_end:
pop es
pop ds
popa
ret

; Runs the MixColumns operation on the plaintext at 0xD0
MixColumns:
pusha
push es
; Copy plaintext to give us something to work with
mov di, 0xE0
mov si, di
mov di, 0xF0
xor cx, cx
    MixColumns_CopyPlaintextLoop:
    mov ax, word [ds:si]
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    mov ax, word [ds:si]
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    add cx, 0x4
    cmp cx, 0x10
    jne MixColumns_CopyPlaintextLoop
; Actual algorithm
mov di, 0xE0
mov si, di
mov di, 0xF0
mov bx, 0x50
mov es, bx
mov bx, MixColumnEncryptMatrix
xor cx, cx
    MixColumns_MixColumnLoop:
    push di
    push si
    push cx
    push bx
    xor cx, cx
	MixColumns_MixRowLoop:
	push di
	push cx
	push bx
	xor ax, ax
	xor dx, dx
	xor cx, cx
	; Add each row to ah and set the row/column to it.
	MixColumns_MixRowLoop_XORAddition:
	; Move ah to the matrix's cooresponding value
	push bx
	mov ah, byte [es:bx]
	cmp ah, 1
	jne MixColumns_MixRowLoop_XORAddition_MultiplyGreaterThanOne
	mov ah, byte [ds:di]
	jmp MixColumns_MixRowLoop_XORAddition_MultiplyByOneSkip
	MixColumns_MixRowLoop_XORAddition_MultiplyGreaterThanOne:
	push cx
	mov cx, MultiplicationLookup3
	mov bx, MultiplicationLookup2
	cmp ah, 3
	cmove bx, cx
	mov dl, byte [ds:di]
	add bx, dx
	mov ah, byte [es:bx]
	pop cx
	MixColumns_MixRowLoop_XORAddition_MultiplyByOneSkip:
	pop bx
	xor al, ah
	inc bx
	inc cx
	add di, 4
	cmp cx, 4
	jne MixColumns_MixRowLoop_XORAddition
	mov [ds:si], byte al
	; Set row-column of key to al
	pop bx
	pop cx
	pop di
	add si, 4
	add bx, 4
	inc cx
	cmp cx, 4
	jne MixColumns_MixRowLoop
    pop bx
    pop cx
    pop si
    pop di
    inc di
    inc si
    inc cx
    cmp cx, 4
    jne MixColumns_MixColumnLoop
MixColumns_End:
pop es
popa
ret

; Runs the MixColumnsDecrypt operation on the plaintext at 0xE0
MixColumnsInverse:
pusha
; Copy plaintext to give us something to work with
mov di, 0xE0
mov si, di
mov di, 0xF0
xor cx, cx
    MixColumnsInverse_CopyPlaintextLoop:
    mov ax, word [ds:si]
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    mov ax, word [ds:si]
    mov [ds:di], word ax
    add si, 0x2
    add di, 0x2
    add cx, 0x4
    cmp cx, 0x10
    jne MixColumnsInverse_CopyPlaintextLoop
; Actual algorithm
mov di, 0xE0
mov si, di
mov di, 0xF0
mov bx, 0x50
mov es, bx
mov bx, MixColumnInverseMatrix
xor cx, cx
    MixColumnsInverse_MixColumnLoop:
    push di
    push si
    push cx
    push bx
    xor cx, cx
	MixColumnsInverse_MixRowLoop:
	push di
	push cx
	push bx
	xor ax, ax
	xor dx, dx
	xor cx, cx
	    ; Add each row to ah and set the row/column to it.
	    MixColumnsInverse_MixRowLoop_XORAddition:
	    ; Move ah to the matrix's cooresponding value
	    push bx
	    push cx
	    mov ah, byte [es:bx]
	    mov bx, MultiplicationLookup9
	    mov cx, MultiplicationLookup11
	    cmp ah, 11
	    cmove bx, cx
	    mov cx, MultiplicationLookup13
	    cmp ah, 13
	    cmove bx, cx
	    mov cx, MultiplicationLookup14
	    cmp ah, 14
	    cmove bx, cx
	    mov dl, byte [ds:di]
	    add bx, dx
	    mov ah, byte [es:bx]
	    pop cx
	    pop bx
	    xor al, ah
	    inc bx
	    inc cx
	    add di, 4
	    cmp cx, 4
	    jne MixColumnsInverse_MixRowLoop_XORAddition
	; Set row-column of key to al
	mov [ds:si], byte al
	pop bx
	pop cx
	pop di
	add si, 4
	add bx, 4
	inc cx
	cmp cx, 4
	jne MixColumnsInverse_MixRowLoop
    pop bx
    pop cx
    pop si
    pop di
    inc di
    inc si
    inc cx
    cmp cx, 4
    jne MixColumnsInverse_MixColumnLoop
MixColumnsInverse_End:
popa
ret

; Generates the Key Schedule
GenerateKeySchedule:
pusha
push es
push ds
mov bx, 0x50 ; Move pointer to the key.
mov es, bx
mov bx, 0x600
mov ds, bx
xor bx, bx
mov di, 0x20
xor cx, cx
    GenerateKeySchedule_ExtendKeyLoop: ; Extend all the keys.
    push di
    push cx
    GenerateKeySchedule_ExtendSingularKey:
	; Do first column uniquely
	; Copy last column to current column and shift it
	; 0 0 0 A   D 0 0 0
	; 0 0 0 B   A 0 0 0
	; 0 0 0 C   B 0 0 0
	; 0 0 0 D   C 0 0 0
	sub di, 9 ; First column
	mov ah, byte [ds:di]
	add di, 9
	mov [ds:di], byte ah
	add di, 0x4 ; Second column
	sub di, 9
	mov ah, byte [ds:di]
	add di, 9
	mov [ds:di], byte ah
	add di, 0x4 ; Third column
	sub di, 9
	mov ah, byte [ds:di]
	add di, 9
	mov [ds:di], byte ah
	add di, 0x4 ; Fourth column
	sub di, 25
	mov ah, byte [ds:di]
	add di, 25
	mov [ds:di], byte ah
	; Run each column through the S-Box
	sub di, 12 ; Begin at first column
	xor ah, ah
	xor cx, cx
	    GenerateKeySchedule_ExtendSingularKey_RunThroughSBoxLoop: ; Run each column through the S-Box
	    mov al, byte [ds:di]
	    xor ah, ah
	    rol al, 4
	    mov bx, SBox
	    add bx, ax
	    mov al, byte [es:bx]
	    mov [ds:di], byte al
	    inc cx
	    add di, 4
	    cmp cx, 4
	    jne GenerateKeySchedule_ExtendSingularKey_RunThroughSBoxLoop
	sub di, 0x10 ; XOR RCon to first column
	mov al, byte [ds:di]
	mov bx, RconValues
	pop cx ; Retrieve round number
	add bx, cx
	inc bx
	push cx
	mov ah, byte [es:bx]
	xor al, ah
	mov [ds:di], byte al
	; XOR column by column prior
	xor cx, cx
	    GenerateKeySchedule_ExtendSingularKey_XORbyColumn: ; For each row, XOR it by the one 16 bytes prior.
	    mov ah, byte [ds:di]
	    sub di, 0x10
	    mov al, byte [ds:di]
	    add di, 0x10
	    xor ah, al
	    mov [ds:di], ah
	    inc cx
	    add di, 4
	    cmp cx, 4
	    jne GenerateKeySchedule_ExtendSingularKey_XORbyColumn
	; The first column has been extended.
	; Loop for the next three columns
	sub di, 0xF
	xor cx, cx
	GenerateKeySchedule_ExtendKeyByColumnLoop:
	pusha
	xor cx, cx
	    GenerateKeySchedule_ExtendKeyByColumnLoop_XORAndCopyByRow: ; For each row, copy previous row and XOR it by the one 16 bytes prior.
	    dec di
	    mov ah, byte [ds:di]
	    sub di, 0xF
	    mov al, byte [ds:di]
	    xor ah, al
	    add di, 0x10
	    mov [ds:di], byte ah
	    inc cx
	    add di, 4
	    cmp cx, 4
	    jne GenerateKeySchedule_ExtendKeyByColumnLoop_XORAndCopyByRow
	popa
	inc cx
	inc di
	cmp cx, 3
	jne GenerateKeySchedule_ExtendKeyByColumnLoop
    pop cx
    pop di
    add di, 0x10
    inc cx
    cmp cx, 12
    jne GenerateKeySchedule_ExtendKeyLoop
GenerateKeySchedule_end:
pop ds
pop es
popa
ret

; # Tables Utilized By AES #
; Inputs:
Plaintext:
db 0xD4, 0xE0, 0xB8, 0x1E
db 0xBF, 0xB4, 0x41, 0x27
db 0x5D, 0x52, 0x11, 0x98
db 0x30, 0xAE, 0xF1, 0xE5
Key:
db 0x28, 0x1B, 0x73, 0x2A
db 0x3E, 0x57, 0xF4, 0x32
db 0x34, 0xDD, 0x59, 0x03
db 0x5E, 0x1A, 0x63, 0xC2
; Second block
db 0x6D, 0x19, 0xF2, 0xC1
db 0x72, 0x22, 0x36, 0xBB
db 0x3D, 0x27, 0x54, 0x6C
db 0xA0, 0x64, 0x11, 0x59
; The key is a total of 32 bytes.
; Algorithm Tables:
RconValues:
db 0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A
MixColumnEncryptMatrix:
db 2, 3, 1, 1
db 1, 2, 3, 1
db 1, 1, 2, 3
db 3, 1, 1, 2
MixColumnInverseMatrix:
db 14, 11, 13, 9
db 9, 14, 11, 13
db 13, 9, 14, 11
db 11, 13, 9, 14
MultiplicationLookup2:
db 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E
db 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E
db 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E
db 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E
db 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E
db 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE
db 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE
db 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE
db 0x1B, 0x19, 0x1F, 0x1D, 0x13, 0x11, 0x17, 0x15, 0x0B, 0x09, 0x0F, 0x0D, 0x03, 0x01, 0x07, 0x05
db 0x3B, 0x39, 0x3F, 0x3D, 0x33, 0x31, 0x37, 0x35, 0x2B, 0x29, 0x2F, 0x2D, 0x23, 0x21, 0x27, 0x25
db 0x5B, 0x59, 0x5F, 0x5D, 0x53, 0x51, 0x57, 0x55, 0x4B, 0x49, 0x4F, 0x4D, 0x43, 0x41, 0x47, 0x45
db 0x7B, 0x79, 0x7F, 0x7D, 0x73, 0x71, 0x77, 0x75, 0x6B, 0x69, 0x6F, 0x6D, 0x63, 0x61, 0x67, 0x65
db 0x9B, 0x99, 0x9F, 0x9D, 0x93, 0x91, 0x97, 0x95, 0x8B, 0x89, 0x8F, 0x8D, 0x83, 0x81, 0x87, 0x85
db 0xBB, 0xB9, 0xBF, 0xBD, 0xB3, 0xB1, 0xB7, 0xB5, 0xAB, 0xA9, 0xAF, 0xAD, 0xA3, 0xA1, 0xA7, 0xA5
db 0xDB, 0xD9, 0xDF, 0xDD, 0xD3, 0xD1, 0xD7, 0xD5, 0xCB, 0xC9, 0xCF, 0xCD, 0xC3, 0xC1, 0xC7, 0xC5
db 0xFB, 0xF9, 0xFF, 0xFD, 0xF3, 0xF1, 0xF7, 0xF5, 0xEB, 0xE9, 0xEF, 0xED, 0xE3, 0xE1, 0xE7, 0xE5
MultiplicationLookup3:
db 0x00, 0x03, 0x06, 0x05, 0x0C, 0x0F, 0x0A, 0x09, 0x18, 0x1B, 0x1E, 0x1D, 0x14, 0x17, 0x12, 0x11
db 0x30, 0x33, 0x36, 0x35, 0x3C, 0x3F, 0x3A, 0x39, 0x28, 0x2B, 0x2E, 0x2D, 0x24, 0x27, 0x22, 0x21
db 0x60, 0x63, 0x66, 0x65, 0x6C, 0x6F, 0x6A, 0x69, 0x78, 0x7B, 0x7E, 0x7D, 0x74, 0x77, 0x72, 0x71
db 0x50, 0x53, 0x56, 0x55, 0x5C, 0x5F, 0x5A, 0x59, 0x48, 0x4B, 0x4E, 0x4D, 0x44, 0x47, 0x42, 0x41
db 0xC0, 0xC3, 0xC6, 0xC5, 0xCC, 0xCF, 0xCA, 0xC9, 0xD8, 0xDB, 0xDE, 0xDD, 0xD4, 0xD7, 0xD2, 0xD1
db 0xF0, 0xF3, 0xF6, 0xF5, 0xFC, 0xFF, 0xFA, 0xF9, 0xE8, 0xEB, 0xEE, 0xED, 0xE4, 0xE7, 0xE2, 0xE1
db 0xA0, 0xA3, 0xA6, 0xA5, 0xAC, 0xAF, 0xAA, 0xA9, 0xB8, 0xBB, 0xBE, 0xBD, 0xB4, 0xB7, 0xB2, 0xB1
db 0x90, 0x93, 0x96, 0x95, 0x9C, 0x9F, 0x9A, 0x99, 0x88, 0x8B, 0x8E, 0x8D, 0x84, 0x87, 0x82, 0x81
db 0x9B, 0x98, 0x9D, 0x9E, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8F, 0x8C, 0x89, 0x8A
db 0xAB, 0xA8, 0xAD, 0xAE, 0xA7, 0xA4, 0xA1, 0xA2, 0xB3, 0xB0, 0xB5, 0xB6, 0xBF, 0xBC, 0xB9, 0xBA
db 0xFB, 0xF8, 0xFD, 0xFE, 0xF7, 0xF4, 0xF1, 0xF2, 0xE3, 0xE0, 0xE5, 0xE6, 0xEF, 0xEC, 0xE9, 0xEA
db 0xCB, 0xC8, 0xCD, 0xCE, 0xC7, 0xC4, 0xC1, 0xC2, 0xD3, 0xD0, 0xD5, 0xD6, 0xDF, 0xDC, 0xD9, 0xDA
db 0x5B, 0x58, 0x5D, 0x5E, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4F, 0x4C, 0x49, 0x4A
db 0x6B, 0x68, 0x6D, 0x6E, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7F, 0x7C, 0x79, 0x7A
db 0x3B, 0x38, 0x3D, 0x3E, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2F, 0x2C, 0x29, 0x2A
db 0x0B, 0x08, 0x0D, 0x0E, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1F, 0x1C, 0x19, 0x1A
MultiplicationLookup9:
db 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77
db 0x90, 0x99, 0x82, 0x8B, 0xB4, 0xBD, 0xA6, 0xAF, 0xD8, 0xD1, 0xCA, 0xC3, 0xFC, 0xF5, 0xEE, 0xE7
db 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C
db 0xAB, 0xA2, 0xB9, 0xB0, 0x8F, 0x86, 0x9D, 0x94, 0xE3, 0xEA, 0xF1, 0xF8, 0xC7, 0xCE, 0xD5, 0xDC
db 0x76, 0x7F, 0x64, 0x6D, 0x52, 0x5B, 0x40, 0x49, 0x3E, 0x37, 0x2C, 0x25, 0x1A, 0x13, 0x08, 0x01
db 0xE6, 0xEF, 0xF4, 0xFD, 0xC2, 0xCB, 0xD0, 0xD9, 0xAE, 0xA7, 0xBC, 0xB5, 0x8A, 0x83, 0x98, 0x91
db 0x4D, 0x44, 0x5F, 0x56, 0x69, 0x60, 0x7B, 0x72, 0x05, 0x0C, 0x17, 0x1E, 0x21, 0x28, 0x33, 0x3A
db 0xDD, 0xD4, 0xCF, 0xC6, 0xF9, 0xF0, 0xEB, 0xE2, 0x95, 0x9C, 0x87, 0x8E, 0xB1, 0xB8, 0xA3, 0xAA
db 0xEC, 0xE5, 0xFE, 0xF7, 0xC8, 0xC1, 0xDA, 0xD3, 0xA4, 0xAD, 0xB6, 0xBF, 0x80, 0x89, 0x92, 0x9B
db 0x7C, 0x75, 0x6E, 0x67, 0x58, 0x51, 0x4A, 0x43, 0x34, 0x3D, 0x26, 0x2F, 0x10, 0x19, 0x02, 0x0B
db 0xD7, 0xDE, 0xC5, 0xCC, 0xF3, 0xFA, 0xE1, 0xE8, 0x9F, 0x96, 0x8D, 0x84, 0xBB, 0xB2, 0xA9, 0xA0
db 0x47, 0x4E, 0x55, 0x5C, 0x63, 0x6A, 0x71, 0x78, 0x0F, 0x06, 0x1D, 0x14, 0x2B, 0x22, 0x39, 0x30
db 0x9A, 0x93, 0x88, 0x81, 0xBE, 0xB7, 0xAC, 0xA5, 0xD2, 0xDB, 0xC0, 0xC9, 0xF6, 0xFF, 0xE4, 0xED
db 0x0A, 0x03, 0x18, 0x11, 0x2E, 0x27, 0x3C, 0x35, 0x42, 0x4B, 0x50, 0x59, 0x66, 0x6F, 0x74, 0x7D
db 0xA1, 0xA8, 0xB3, 0xBA, 0x85, 0x8C, 0x97, 0x9E, 0xE9, 0xE0, 0xFB, 0xF2, 0xCD, 0xC4, 0xDF, 0xD6
db 0x31, 0x38, 0x23, 0x2A, 0x15, 0x1C, 0x07, 0x0E, 0x79, 0x70, 0x6B, 0x62, 0x5D, 0x54, 0x4F, 0x46
MultiplicationLookup11:
db 0x00, 0x0B, 0x16, 0x1D, 0x2C, 0x27, 0x3A, 0x31, 0x58, 0x53, 0x4E, 0x45, 0x74, 0x7F, 0x62, 0x69
db 0xB0, 0xBB, 0xA6, 0xAD, 0x9C, 0x97, 0x8A, 0x81, 0xE8, 0xE3, 0xFE, 0xF5, 0xC4, 0xCF, 0xD2, 0xD9
db 0x7B, 0x70, 0x6D, 0x66, 0x57, 0x5C, 0x41, 0x4A, 0x23, 0x28, 0x35, 0x3E, 0x0F, 0x04, 0x19, 0x12
db 0xCB, 0xC0, 0xDD, 0xD6, 0xE7, 0xEC, 0xF1, 0xFA, 0x93, 0x98, 0x85, 0x8E, 0xBF, 0xB4, 0xA9, 0xA2
db 0xF6, 0xFD, 0xE0, 0xEB, 0xDA, 0xD1, 0xCC, 0xC7, 0xAE, 0xA5, 0xB8, 0xB3, 0x82, 0x89, 0x94, 0x9F
db 0x46, 0x4D, 0x50, 0x5B, 0x6A, 0x61, 0x7C, 0x77, 0x1E, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2F
db 0x8D, 0x86, 0x9B, 0x90, 0xA1, 0xAA, 0xB7, 0xBC, 0xD5, 0xDE, 0xC3, 0xC8, 0xF9, 0xF2, 0xEF, 0xE4
db 0x3D, 0x36, 0x2B, 0x20, 0x11, 0x1A, 0x07, 0x0C, 0x65, 0x6E, 0x73, 0x78, 0x49, 0x42, 0x5F, 0x54
db 0xF7, 0xFC, 0xE1, 0xEA, 0xDB, 0xD0, 0xCD, 0xC6, 0xAF, 0xA4, 0xB9, 0xB2, 0x83, 0x88, 0x95, 0x9E
db 0x47, 0x4C, 0x51, 0x5A, 0x6B, 0x60, 0x7D, 0x76, 0x1F, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2E
db 0x8C, 0x87, 0x9A, 0x91, 0xA0, 0xAB, 0xB6, 0xBD, 0xD4, 0xDF, 0xC2, 0xC9, 0xF8, 0xF3, 0xEE, 0xE5
db 0x3C, 0x37, 0x2A, 0x21, 0x10, 0x1B, 0x06, 0x0D, 0x64, 0x6F, 0x72, 0x79, 0x48, 0x43, 0x5E, 0x55
db 0x01, 0x0A, 0x17, 0x1C, 0x2D, 0x26, 0x3B, 0x30, 0x59, 0x52, 0x4F, 0x44, 0x75, 0x7E, 0x63, 0x68
db 0xB1, 0xBA, 0xA7, 0xAC, 0x9D, 0x96, 0x8B, 0x80, 0xE9, 0xE2, 0xFF, 0xF4, 0xC5, 0xCE, 0xD3, 0xD8
db 0x7A, 0x71, 0x6C, 0x67, 0x56, 0x5D, 0x40, 0x4B, 0x22, 0x29, 0x34, 0x3F, 0x0E, 0x05, 0x18, 0x13
db 0xCA, 0xC1, 0xDC, 0xD7, 0xE6, 0xED, 0xF0, 0xFB, 0x92, 0x99, 0x84, 0x8F, 0xBE, 0xB5, 0xA8, 0xA3
MultiplicationLookup13:
db 0x00, 0x0D, 0x1A, 0x17, 0x34, 0x39, 0x2E, 0x23, 0x68, 0x65, 0x72, 0x7F, 0x5C, 0x51, 0x46, 0x4B
db 0xD0, 0xDD, 0xCA, 0xC7, 0xE4, 0xE9, 0xFE, 0xF3, 0xB8, 0xB5, 0xA2, 0xAF, 0x8C, 0x81, 0x96, 0x9B
db 0xBB, 0xB6, 0xA1, 0xAC, 0x8F, 0x82, 0x95, 0x98, 0xD3, 0xDE, 0xC9, 0xC4, 0xE7, 0xEA, 0xFD, 0xF0
db 0x6B, 0x66, 0x71, 0x7C, 0x5F, 0x52, 0x45, 0x48, 0x03, 0x0E, 0x19, 0x14, 0x37, 0x3A, 0x2D, 0x20
db 0x6D, 0x60, 0x77, 0x7A, 0x59, 0x54, 0x43, 0x4E, 0x05, 0x08, 0x1F, 0x12, 0x31, 0x3C, 0x2B, 0x26
db 0xBD, 0xB0, 0xA7, 0xAA, 0x89, 0x84, 0x93, 0x9E, 0xD5, 0xD8, 0xCF, 0xC2, 0xE1, 0xEC, 0xFB, 0xF6
db 0xD6, 0xDB, 0xCC, 0xC1, 0xE2, 0xEF, 0xF8, 0xF5, 0xBE, 0xB3, 0xA4, 0xA9, 0x8A, 0x87, 0x90, 0x9D
db 0x06, 0x0B, 0x1C, 0x11, 0x32, 0x3F, 0x28, 0x25, 0x6E, 0x63, 0x74, 0x79, 0x5A, 0x57, 0x40, 0x4D
db 0xDA, 0xD7, 0xC0, 0xCD, 0xEE, 0xE3, 0xF4, 0xF9, 0xB2, 0xBF, 0xA8, 0xA5, 0x86, 0x8B, 0x9C, 0x91
db 0x0A, 0x07, 0x10, 0x1D, 0x3E, 0x33, 0x24, 0x29, 0x62, 0x6F, 0x78, 0x75, 0x56, 0x5B, 0x4C, 0x41
db 0x61, 0x6C, 0x7B, 0x76, 0x55, 0x58, 0x4F, 0x42, 0x09, 0x04, 0x13, 0x1E, 0x3D, 0x30, 0x27, 0x2A
db 0xB1, 0xBC, 0xAB, 0xA6, 0x85, 0x88, 0x9F, 0x92, 0xD9, 0xD4, 0xC3, 0xCE, 0xED, 0xE0, 0xF7, 0xFA
db 0xB7, 0xBA, 0xAD, 0xA0, 0x83, 0x8E, 0x99, 0x94, 0xDF, 0xD2, 0xC5, 0xC8, 0xEB, 0xE6, 0xF1, 0xFC
db 0x67, 0x6A, 0x7D, 0x70, 0x53, 0x5E, 0x49, 0x44, 0x0F, 0x02, 0x15, 0x18, 0x3B, 0x36, 0x21, 0x2C
db 0x0C, 0x01, 0x16, 0x1B, 0x38, 0x35, 0x22, 0x2F, 0x64, 0x69, 0x7E, 0x73, 0x50, 0x5D, 0x4A, 0x47
db 0xDC, 0xD1, 0xC6, 0xCB, 0xE8, 0xE5, 0xF2, 0xFF, 0xB4, 0xB9, 0xAE, 0xA3, 0x80, 0x8D, 0x9A, 0x97
MultiplicationLookup14:
db 0x00, 0x0E, 0x1C, 0x12, 0x38, 0x36, 0x24, 0x2A, 0x70, 0x7E, 0x6C, 0x62, 0x48, 0x46, 0x54, 0x5A
db 0xE0, 0xEE, 0xFC, 0xF2, 0xD8, 0xD6, 0xC4, 0xCA, 0x90, 0x9E, 0x8C, 0x82, 0xA8, 0xA6, 0xB4, 0xBA
db 0xDB, 0xD5, 0xC7, 0xC9, 0xE3, 0xED, 0xFF, 0xF1, 0xAB, 0xA5, 0xB7, 0xB9, 0x93, 0x9D, 0x8F, 0x81
db 0x3B, 0x35, 0x27, 0x29, 0x03, 0x0D, 0x1F, 0x11, 0x4B, 0x45, 0x57, 0x59, 0x73, 0x7D, 0x6F, 0x61
db 0xAD, 0xA3, 0xB1, 0xBF, 0x95, 0x9B, 0x89, 0x87, 0xDD, 0xD3, 0xC1, 0xCF, 0xE5, 0xEB, 0xF9, 0xF7
db 0x4D, 0x43, 0x51, 0x5F, 0x75, 0x7B, 0x69, 0x67, 0x3D, 0x33, 0x21, 0x2F, 0x05, 0x0B, 0x19, 0x17
db 0x76, 0x78, 0x6A, 0x64, 0x4E, 0x40, 0x52, 0x5C, 0x06, 0x08, 0x1A, 0x14, 0x3E, 0x30, 0x22, 0x2C
db 0x96, 0x98, 0x8A, 0x84, 0xAE, 0xA0, 0xB2, 0xBC, 0xE6, 0xE8, 0xFA, 0xF4, 0xDE, 0xD0, 0xC2, 0xCC
db 0x41, 0x4F, 0x5D, 0x53, 0x79, 0x77, 0x65, 0x6B, 0x31, 0x3F, 0x2D, 0x23, 0x09, 0x07, 0x15, 0x1B
db 0xA1, 0xAF, 0xBD, 0xB3, 0x99, 0x97, 0x85, 0x8B, 0xD1, 0xDF, 0xCD, 0xC3, 0xE9, 0xE7, 0xF5, 0xFB
db 0x9A, 0x94, 0x86, 0x88, 0xA2, 0xAC, 0xBE, 0xB0, 0xEA, 0xE4, 0xF6, 0xF8, 0xD2, 0xDC, 0xCE, 0xC0
db 0x7A, 0x74, 0x66, 0x68, 0x42, 0x4C, 0x5E, 0x50, 0x0A, 0x04, 0x16, 0x18, 0x32, 0x3C, 0x2E, 0x20
db 0xEC, 0xE2, 0xF0, 0xFE, 0xD4, 0xDA, 0xC8, 0xC6, 0x9C, 0x92, 0x80, 0x8E, 0xA4, 0xAA, 0xB8, 0xB6
db 0x0C, 0x02, 0x10, 0x1E, 0x34, 0x3A, 0x28, 0x26, 0x7C, 0x72, 0x60, 0x6E, 0x44, 0x4A, 0x58, 0x56
db 0x37, 0x39, 0x2B, 0x25, 0x0F, 0x01, 0x13, 0x1D, 0x47, 0x49, 0x5B, 0x55, 0x7F, 0x71, 0x63, 0x6D
db 0xD7, 0xD9, 0xCB, 0xC5, 0xEF, 0xE1, 0xF3, 0xFD, 0xA7, 0xA9, 0xBB, 0xB5, 0x9F, 0x91, 0x83, 0x8D
SBox:
db 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76
db 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0
db 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15
db 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75
db 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84
db 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF
db 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8
db 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2
db 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73
db 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB
db 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79
db 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08
db 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A
db 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E
db 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF
db 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
InverseSBox:
db 0x25, 0xC7, 0x45, 0x80, 0x27, 0xC6, 0x09, 0x0D, 0xA3, 0x69, 0x74, 0xCF, 0xF1, 0x06, 0x0A, 0x71
db 0x90, 0x3E, 0xB7, 0xE2, 0x8F, 0x07, 0x8D, 0xC2, 0x19, 0xCA, 0x1F, 0x65, 0xDD, 0x15, 0x0E, 0xB2
db 0xA6, 0x93, 0x49, 0x1A, 0x6F, 0x84, 0xBA, 0xE1, 0x11, 0x47, 0xA1, 0xE3, 0x8A, 0xF7, 0xB3, 0x40
db 0x5D, 0x28, 0x23, 0x66, 0x46, 0x05, 0x00, 0xF8, 0x14, 0x22, 0x17, 0xB4, 0x33, 0x9A, 0xD4, 0xE7
db 0x03, 0xB9, 0x6A, 0x82, 0x68, 0xDF, 0xC8, 0xAC, 0xF4, 0x7E, 0xD1, 0x6C, 0x88, 0x91, 0xEA, 0xAB
db 0x63, 0xF2, 0x2C, 0x9D, 0x86, 0xDE, 0xCB, 0xF3, 0x76, 0xDA, 0x92, 0x2D, 0x70, 0x5B, 0xA2, 0x77
db 0x5A, 0xFF, 0x32, 0x42, 0x89, 0x9B, 0x3D, 0xF0, 0xCD, 0x53, 0x5C, 0x97, 0x7C, 0xA4, 0x5F, 0x6D
db 0x83, 0x78, 0xD3, 0x2B, 0x61, 0xAD, 0xA0, 0x20, 0xAE, 0x58, 0x98, 0x02, 0x13, 0xD0, 0x0B, 0x62
db 0xFB, 0x43, 0xEE, 0x67, 0x4D, 0xE5, 0x7F, 0x1C, 0x79, 0x2E, 0xF6, 0xA9, 0x1B, 0xD2, 0x8C, 0x1E
db 0x04, 0xE8, 0xC4, 0xB5, 0x4A, 0x51, 0x4E, 0xFA, 0x2F, 0x9F, 0x7B, 0xBD, 0x21, 0x5E, 0xBE, 0x96
db 0x3A, 0x34, 0x59, 0x2A, 0xC5, 0x64, 0x85, 0xDB, 0xFC, 0x73, 0x26, 0x0C, 0x01, 0xA7, 0xBB, 0x41
db 0xE9, 0x44, 0xB0, 0x94, 0xCC, 0x75, 0x50, 0x30, 0xEC, 0x8E, 0xE0, 0xEF, 0x95, 0xF9, 0xC3, 0x36
db 0x18, 0x4C, 0x24, 0xD6, 0xD5, 0x7A, 0x8B, 0x10, 0x0F, 0xC1, 0xAA, 0x87, 0x72, 0x39, 0x38, 0x55
db 0x3F, 0xED, 0xAF, 0xB8, 0x56, 0xD8, 0x3B, 0x31, 0x4B, 0x57, 0x81, 0xDC, 0x08, 0x9C, 0x35, 0x12
db 0x7D, 0x9E, 0x3C, 0x1D, 0x6B, 0xD9, 0x54, 0xA8, 0x6E, 0xFD, 0xEB, 0xA5, 0xCE, 0xC9, 0x99, 0xC0
db 0xBF, 0xBC, 0xE4, 0x52, 0x29, 0x48, 0x60, 0xB6, 0x37, 0xE6, 0xB1, 0x4F, 0xF5, 0xFE, 0x16, 0xD7

; # Generic Module Footer #

; times 8192 - ($ - $$) db 0